#!/usr/bin/python

# Based on edit.py example from urwid-0.9.8.4 by Ian Ward (C) 2004 - 2009
# Changed by Gustaw Lewandowski 2011
# Licence : GPL 2.1

import urwid
import types
import ucp
import utils
from utils import log, dict

lexer = None

class LineNR(urwid.AttrWrap):
	def __init__(self, nr):
		widget = urwid.Text(nr)
		self.__super.__init__(widget , 'linenr')

class EditLine(urwid.Edit):
	def __init__(self, text = ''):
		self.attr_norm	= 'edit'
		self.attr_wrong	= 'errortxt'
		self.attr_tab	= 'foot'
		self.tab_size	= 8
		self.__super.__init__('', '', allow_tab=False)
		self.set_real_text(text)
	
	def move_cursor_to_coords(self, size, x, y):
		"""Need to set proper pos in case tab"""
		if x > 0:
			return self.__super.move_cursor_to_coords(size, self.get_move_right(x), y)
		return self.__super.move_cursor_to_coords(size, x, y)
	
	def set_real_text(self, text):
		newTable = []
		s = ''
		newText = ''
		p = 0
		for c in text:
			if c == '\t':
				if len(s) != 0:
					newTable.append((self.attr_norm, s))
					p += len(s)
					newText += s
				tab_str = self.get_tab_str(p)
				newTable.append((self.attr_tab, tab_str))
				p += len(tab_str)
				newText += tab_str
				s = ''
			else:
				s += c
		if len(s) == 0:
			s = ''
		newTable.append((self.attr_norm, s))
		newText += s

		urwid.Text.set_text(self, newTable)
		self._edit_text = newText # result_text
		self._edit_pos += len(newText)
		self.highlight = None
		self.pref_col_maxcol = None, None
		self._invalidate()
	
	def str_split(self, s):
		seps = [' ', ',', '.', ':']
		ret = []
		sep_mode = False
		word = ''
		sep = ''
		for c in s:
			if sep_mode == False:
				if c in seps:
					sep_mode = True
					sep = c
				else:
					word += c
			else:
				if c in seps:
					sep += c
				else:
					ret.append((word, sep))
					sep_mode = False
					sep = ''
					word = c
		ret.append((word, sep))
		return ret

	def render(self, size, focus=False):
		#txt, tbl = self.get_text()
		#log.info(lex(txt, lexer))
		return self.__super.render(size, focus)
		if dict == None:
			return self.__super.render(size, focus)
		txt, tbl = self.get_text()
		l = 0
		p = 0
		s = ""
		new_table = []
		for i in tbl:
			l += i[1]
			s = txt[p:l]
			# trzeba cos zrobic a by analizowac text z attrn i attrw razem
			if i[0] != self.attr_tab:
				for w in self.str_split(s):
					if len(w[0]) == 0:
						new_table.append((self.attr_norm, w[1]))
					elif dict.check(w[0]) == True:
						new_table.append((self.attr_norm, w[0]+w[1]))
					else:
						new_table.append((self.attr_wrong, w[0]+w[1]))
			else:
				new_table.append((i[0], s))
			p = l
		urwid.Text.set_text(self, new_table)
		return self.__super.render(size, focus)

	def get_real_text(self):
		txt, tbl = self.get_text()
		l = 0
		p = 0
		s = ""
		for i in tbl:
			l += i[1]
			if i[0] == self.attr_tab:
				size = i[1]
				while (size > 0):
					s += '\t'
					size -= self.tab_size
			else:
				s += txt[p:l]
			p = l
		return s

	def get_real_text_to(self, pos = None):
		if pos == None:
			pos = self.edit_pos
		s = self.get_real_text()
		l = 0
		r = ""
		for c in s:
			if c == '\t':
				l += self.tab_size - (l % self.tab_size)
			else:
				l += 1
			r += c
			if l >= pos:
				break
		return r

	def get_real_text_from(self, pos = None):
		if pos == None:
			pos = self.edit_pos
		s = self.get_real_text()
		l = 0
		r = ""
		for c in s:
			if c == '\t':
				l += self.tab_size - (l % self.tab_size)
			else:
				l += 1
			if l > pos:
				r += c
		return r

	def get_tab_str(self, pos):
		""" get single tab str for given position """
		l = self.tab_size - (pos % self.tab_size)
		if l == 1:
			return ">"
		elif l == 2:
			return "<>"
		else:
			return "<" + "-" * (l - 2) + ">"

	def get_tabs_str(self, pos, size):
		""" get tabs str for given pos and size, but size is given only
		for compute count of tabs """
		cnt = 0
		while (size > 0):
			cnt += 1
			size -= self.tab_size
		s = ""
		for i in range(0, cnt):
			s += self.get_tab_str(pos)
			pos = 0
		return s

	def get_attr(self, pos = None):
		""" get current attr (based on pos or self.edit_pos) """
		if pos == None:
			pos = self.edit_pos
		txt, tbl = self.get_text()
		i = None
		l = 0
		p = 0
		for i in tbl:
			l += i[1]
			if l > pos:
				return (pos - p, i)
			p = l
		return (0, i)

	def get_attr_next(self):
		txt, tbl = self.get_text()
		l = 0
		next = False
		for i in tbl:
			if next == True:
				return i
			l += i[1]
			if l > self.edit_pos:
				next = True
		return None

	def get_attr_prev(self):
		txt, tbl = self.get_text()
		l = 0
		p = None
		for i in tbl:
			l += i[1]
			if l > self.edit_pos:
				return p
			p = i
		return p

	def get_move_left(self, pos = None):
		if pos == None:
			pos = self.edit_pos
		pos, attr = self.get_attr(pos)
		if attr == None:
			return 0
		if attr[0] == self.attr_tab and pos != 0:
			if pos >= self.tab_size:
				return self.tab_size
			return pos
		else:
			if pos == 0:
				prev = self.get_attr_prev()
				if prev is not None and prev[0] == self.attr_tab:
					if prev[1] > self.tab_size:
						return self.tab_size
					return prev[1]
			return 1

	def get_move_right(self, pos = None):
		pos, attr = self.get_attr(pos)
		if attr == None:
			return 0
		if attr[0] == self.attr_tab:
			if pos != 0 or (attr[1] % self.tab_size) == 0:
				return self.tab_size
			return (attr[1] % self.tab_size)
		else:
			return 1

	def keypress(self, size, key):
		if key == 'tab':
			self.insert_text(self.get_tab_str(self.edit_pos), self.attr_tab)
		elif key == 'left':
			self.set_edit_pos(self.edit_pos - self.get_move_left())
		elif key == 'right':
			self.set_edit_pos(self.edit_pos + self.get_move_right())
		elif key == 'backspace' and self.edit_pos != 0:
			# we need to store "move" because this value would change after
			# calling del_at_pos
			move = self.get_move_left()
			self.del_at_pos(self.edit_pos - move)
			self.set_edit_pos(self.edit_pos - move)
		elif key == 'delete':
			self.del_at_pos(self.edit_pos)
		else:
			return self.__super.keypress(size, key)
	
	def del_at_pos(self, pos):
		txt, tbl = self.get_text()
		newTable = []
		l = 0
		p = 0
		s = ""
		result_text = ""
		found = False
		check_next_tab = False
		for i in tbl:
			l += i[1]
			s = txt[p:l]
			if found == False and l > pos:
				found = True
				pleft = pos - p
				if i[0] == self.attr_tab:
					if i[1] > self.tab_size and pleft == 0:
						# in case multiple tabs string we del the
						# second one because first would be shorter
						# and we should presave it's size
						pos += self.get_move_right(pos)
						pleft = pos - p
				else:
					check_next_tab = True

				s = s[:pleft] + s[pleft + self.get_move_right(pos):]
			elif check_next_tab == True:
				check_next_tab = False
				if i[0] == self.attr_tab:
					s = self.get_tabs_str(self.edit_pos - 1, len(s))
			if len(s) != 0:
				newTable.append((i[0], s))
				result_text += s
			p = l

		self.pref_col_maxcol = None, None
		if len(newTable) == 0:
			newTable.append((self.attr_norm, ''))
		urwid.Text.set_text(self, newTable)
		self._edit_text = result_text
		self.highlight = None
		self._invalidate()

	def insert_text(self, text, attr=None):
		txt, tbl = self.get_text()
		newTable = []
		l = 0
		p = 0
		s = ""
		result_text = ""
		found = False
		check_next_tab = False
		for i in tbl:
			l += i[1]
			s = txt[p:l]
			if found == False and l > self.edit_pos:
				found = True
				pos = self.edit_pos - p
				if attr == i[0] or (attr != self.attr_tab and i[0] == self.attr_norm):
					if attr == self.attr_tab and i[1] > self.tab_size and pos == 0:
						# in case multiple tabs string we add the
						# second one because first would be shorter
						# and we should presave it's size
						pos += self.get_move_right(self.edit_pos)
						new_str = s[:pos] + self.get_tab_str(0) + s[pos:]
					else:
						check_next_tab = True
						new_str = s[:pos] + text + s[pos:]
					result_text += new_str
					newTable.append((i[0], new_str))
				else:
					if pos != 0:
						newTable.append((i[0], s[:pos]))
						result_text += s[:pos]
					if attr == None:
						newTable.append((self.attr_norm, text))
					else:
						newTable.append((attr, text))
					result_text += text
					if pos != len(s):
						if i[0] == self.attr_tab:
							new_tab_str = self.get_tabs_str(self.edit_pos + len(text), len(s[pos:]))
							newTable.append((i[0], new_tab_str))
							result_text += new_tab_str
						else:
							newTable.append((i[0], s[pos:]))
							result_text += s[pos:]
			elif check_next_tab == True:
				check_next_tab = False
				if i[0] == self.attr_tab:
					new_tab_str = self.get_tabs_str(p + 1, len(s))
					newTable.append((self.attr_tab, new_tab_str))
					result_text += new_tab_str
				else:
					newTable.append((self.attr_norm, s))
					result_text += s
			else:
				if len(s) != 0:
					newTable.append((i[0], s))
					result_text += s
			p = l

		if found == False:
			result_text += text
			if attr == None:
				newTable.append((self.attr_norm, text))
			else:
				newTable.append((attr, text))
		
		urwid.Text.set_text(self, newTable)
		self._edit_text = result_text
		self._edit_pos += len(text)
		self.highlight = None
		self.pref_col_maxcol = None, None
		self._invalidate()

class EditLineWalker(urwid.ListWalker):
	"""ListWalker-compatible class for lazily reading file contents."""
	def __init__(self, name):
		self.file = open(name)
		self.lines = []
		self.focus = 0
		if utils.pygments_enable == True:
			from pygments.lexers import guess_lexer_for_filename
			lexer = guess_lexer_for_filename(name, "")
			log.info(lexer)
			from pygments import lex

	def get_focus(self): 
		return self._get_at_pos(self.focus)

	def set_focus(self, focus):
		self.focus = focus
		self._modified()

	def get_next(self, start_from):
		return self._get_at_pos(start_from + 1)

	def get_prev(self, start_from):
		return self._get_at_pos(start_from - 1)

	def read_next_line(self):
		"""Read another line from the file."""
		next_line = self.file.readline()
		if not next_line or next_line[-1:] != '\n':
			# no newline on last line of file
			self.file = None
		else:
			# trim newline characters
			next_line = next_line[:-1]

		edit = EditLine(next_line)
		self.lines.append(edit)
		return next_line

	def _get_at_pos(self, pos):
		"""Return a widget for the line number passed."""

		if pos < 0:
			# line 0 is the start of the file, no more above
			return None, None
			
		if len(self.lines) > pos:
			# we have that line so return it
			return self.lines[pos], pos

		if self.file is None:
			# file is closed, so there are no more lines
			return None, None

		assert pos == len(self.lines), "out of order request?"

		self.read_next_line()
		return self.lines[-1], pos

	def split_focus(self):
		"""Divide the focus edit widget at the cursor location."""
		focus = self.lines[self.focus]
		if focus.edit_pos == 0:
			self.lines.insert(self.focus, EditLine())
		else:
			edit = EditLine(focus.get_real_text_from())
			focus.set_real_text(focus.get_real_text_to())
			self.lines.insert(self.focus + 1, edit)
		self.set_focus(self.focus + 1)

	def combine_focus_with_prev(self):
		"""Combine the focus edit widget with the one above."""
		focus = self.lines[self.focus]
		if focus.edit_pos != 0:
			return False
		above, ignore = self.get_prev(self.focus)
		if above is None:
			# already at the top
			return False
		old_pos = above.edit_pos
		above.set_real_text(above.get_real_text() + focus.get_real_text())
		del self.lines[self.focus]
		self.set_focus(self.focus - 1)
		above.set_edit_pos(old_pos)
		return True

	def combine_focus_with_next(self):
		"""Combine the focus edit widget with the one below."""
		focus = self.lines[self.focus]
		if focus.edit_pos != len(focus.edit_text):
			return False
		below, ignore = self.get_next(self.focus)
		if below is None:
			# already at bottom
			return False
		focus.set_real_text(focus.get_real_text() + below.get_real_text())
		del self.lines[self.focus + 1]
		return True

class EditArea(urwid.ListBox):
	def __init__(self, name, old_body=None):
		self.old_body = old_body
		self.save_name = name
		self.__super.__init__(EditLineWalker(name))

	def keypress(self,(maxcol,maxrow), key):
		if key in ('f10', 'esc'):
			return (ucp.urwidcmd, ucp.UrwidCmdProc.restore, self.old_body)
		elif key == "delete" and self.body.combine_focus_with_next():
			return None
		elif key == "backspace" and self.body.combine_focus_with_prev():
			return None
		elif key == "enter":
			# start new line
			self.body.split_focus()
			return None
		elif key == 'f2':
			self.save_file()
			return None
		self.__super.keypress((maxcol,maxrow), key)

	def save_file(self):
		"""Write the file out to disk."""

		l = []
		walk = self.body
		for edit in walk.lines:
			l.append(edit.get_real_text())

		# then the rest
		while walk.file is not None:
			l.append(walk.read_next_line())

		# write back to disk
		outfile = open(self.save_name, 'w')

		prefix = ''
		for line in l:
			outfile.write(prefix + line)
			prefix = '\n'

class EditPanel(urwid.AttrWrap):
	def __init__(self, name, old_body=None):
		w = utils.Border(EditArea(name, old_body), name, 'dir')
		self.__super.__init__(w, 'file')

__all__ = ['EditPanel']
